#ifndef STRBLOBLIKEVALUE_H
#define STRBLOBLIKEVALUE_H

#include <vector>
#include <string>
#include <memory>
#include <initializer_list>
#include <stdexcept>

using namespace std;

// 提前声明,StrBlob 中的友元类声明
class StrBlobPtr;

class StrBlob {
    friend class StrBlotPtr;
public:
    typedef vector<string>::size_type size_type;
    StrBlob();
    StrBlob(initializer_list<string> il);
    StrBlob(vector<string> *p);
    StrBlob(StrBlob &s);
    StrBlob& operator=(StrBlob &);
    size_type size() const { return data->size(); }
    bool empty() const { return data->empty(); }
    // 添加和删除元素
    void push_back(const string &t) { data->push_back(t); }
    void pop_back();
    // 元素访问
    string& front();
    const string& front() const;
    string& back();
    const string& back() const;
    // 提供给 StrBlobPtr 的接口
    StrBlobPtr begin(); // 定义 StrBlobPtr 之后才能定义这两个函数
    StrBlobPtr begin() const;
    StrBlobPtr end();
    StrBlobPtr end() const;
private:
    shared_ptr<vector<string>> data;
    // 如果 data[i] 不合法,抛出一个异常
    void check(size_type i, const string &msg) const;
};

inline StrBlob::StrBlob() :
    data(make_shared<vector<string>>()) { }

inline StrBlob::StrBlob(initializer_list<string> il) :
    data(make_shared<vector<string>>(il)) { }

inline StrBlob::StrBlob(vector<string> *p) :
    data(p) { }

inline StrBlob::StrBlob(StrBlob &s) :
    data(make_shared<vector<string>>(*s.data)) { }

StrBlob& StrBlob::operator =(StrBlob &rhs)
{
    data = make_shared<vector<string>>(*rhs.data);
    return *this;
}

void StrBlob::check(size_type i, const string &msg) const
{
    if (i >= data->size()) {
        throw out_of_range(msg);
    }
}

string& StrBlob::front()
{
    check(0, "front on empty StrBlob");
    return data->front();
}

const string& StrBlob::front() const
{
    check(0, "front on empty StrBlob");
    return data->front();
}

string& StrBlob::back()
{
    check(0, "back on empty StrBlob");
    return data->back();
}

const string& StrBlob::back() const
{
    check(0, "back on empty StrBlob");
    return data->back();
}

void StrBlob::pop_back()
{
    check(0, "pop_back on empty StrBlot");
    data->pop_back();
}

// 当试图访问一个不存在的元素时,StrBlobPtr 抛出一个异常
class StrBlobPtr {
    friend eq(const StrBlobPtr&, const StrBlobPtr&);
public:
    StrBlobPtr() : curr(0) { }
    StrBlobPtr(StrBlob &a, size_t sz = 0) :
        wptr(a.data), curr(sz) { }
    StrBlobPtr(const StrBlob &a, size_t sz = 0) :
        wptr(a.data), curr(sz) { }
    string& deref() const;
    string& deref(int off) const;
    StrBlobPtr& incr(); // 前缀递增
    StrBlobPtr& decr(); // 后缀递减
private:
    // 若检查成功，check 返回一个指向 vector 的 shared_ptr
    shared_ptr<vector<string>> check(size_t, const string&) const;
    // 保存一个 weak_ptr,意味着底层 vector 可能会被销毁
    weak_ptr<vector<string>> wptr;
    size_t curr;    // 在数组中当前位置
};

shared_ptr<vector<string>>
StrBlobPtr::check(size_t i, const string &msg) const
{
    auto ret = wptr.lock(); // vector 还存在么？
    if (!ret) {
        throw runtime_error("unbound StrBlobPtr");
    }
    if (i >= ret->size()) {
        throw out_of_range(msg);
    }
    return ret; // 否则，返回指向 vector 的 shared_ptr
}

string& StrBlobPtr::deref() const
{
    auto p = check(curr, "dereference past end");
    return (*p)[curr];  // (*p) 是对象所指的 vector
}

string& StrBlobPtr::deref(int off) const
{
    auto p = check(curr + off, "dereference past end");
    return (*p)[curr + off];  // (*p) 是对象所指的 vector
}

// 前缀递增,返回递增后的对象的引用
StrBlobPtr& StrBlobPtr::incr()
{
    // 如果 curr 已经指向容器的尾后位置,就不能再自增它
    check(curr, "increment past end of StrBlobPtr");
    ++curr; // 推进当前位置
    return *this;
}

// 前缀递减，返回递减后的对象的引用
StrBlobPtr& StrBlobPtr::decr()
{
    --curr;
    check(-1, "decrement past end of StrBlobPtr");
    return *this;
}

// StrBlob 的 begin() 和 end() 定义
StrBlobPtr StrBlob::begin()
{
    return StrBlobPtr(*this);
}

StrBlobPtr StrBlob::begin() const
{
    return StrBlobPtr(*this);
}

StrBlobPtr StrBlob::end()
{
    return StrBlobPtr(*this, data->size());
}

StrBlobPtr StrBlob::end() const
{
    return StrBlobPtr(*this, data->size());
}

// StrBlobPtr 的比较操作
bool eq(const StrBlobPtr &lhs, const StrBlobPtr &rhs)
{
    auto l = lhs.wptr.lock(), r = rhs.wptr.lock();
    // 若底层 vector 是同一个
    if (l == r) {
        // 则两个指针都是空,或者指向相同的元素，它们相等
        return (!r || lhs.curr == rhs.curr);
    } else {
        return false;   // 若指向不同的 vector,则不可能相等
    }
}

bool neq(const StrBlobPtr &lhs, const StrBlobPtr &rhs)
{
    return !eq(lhs, rhs);
}

#endif // STRBLOBLIKEVALUE_H
